package com.zf.fastexcel.utils;

import cn.idev.excel.FastExcel;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.http.HttpHeaders;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;

/**
 * 生成多个 excel, 写入 zip 压缩包中
 *
 * @author ZHOU FENG
 */
public class ExcelToZipUtils {

    private static final String CONTENT_TYPE = "application/zip";
    private static final String ENCODE = StandardCharsets.UTF_8.name();


    /**
     * 导出时封装response
     *
     * @param exportFileName 导出的文件名
     * @param response       响应对象
     */
    public static void decorateResponse(String exportFileName, HttpServletResponse response) {
        // 指定响应内容的类型
        response.setContentType(CONTENT_TYPE);
        // 指定相依的字符编码
        response.setCharacterEncoding(ENCODE);
        // 这里URLEncoder.encode可以防止中文乱码
        String fileName = URLEncoder.encode(exportFileName, StandardCharsets.UTF_8)
                .replaceAll("\\+", "%20");
        // 暴露出 Content-Disposition 响应头，防止由于跨域而访问不到
        response.setHeader(HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, HttpHeaders.CONTENT_DISPOSITION);
        // 设置 Content-Disposition 响应头的值
        response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment;filename*=utf-8''" + fileName + ".zip");
    }


    /**
     * 同时导出多个Excel, 然后将他们写入到一个 zip 压缩包中
     *
     * @param filename           压缩包文件名，不需要后缀
     * @param response           响应
     * @param multiExcelNameList 导出的多个excel的名字 列表
     * @param multiExcelByteList 导出的多个excel的二进制字节 列表
     */
    public static void exportMultiExcelToZip(String filename, HttpServletResponse response, List<String> multiExcelNameList, List<byte[]> multiExcelByteList) throws IOException {
        // 处理响应头
        decorateResponse(filename, response);

        // zip 输出流, 将zip写到响应流中
        ZipOutputStream zipOutputStream = new ZipOutputStream(response.getOutputStream());

        // 循环写入所有的 excel
        for (int i = 0; i < multiExcelNameList.size(); i++) {
            // excel的文件名称
            String itemFilename = multiExcelNameList.get(i);
            // excel的二进制
            byte[] itemExcelByte = multiExcelByteList.get(i);

            // 向 zip 中添加一个条目(文件)
            ZipEntry zipEntry = new ZipEntry(itemFilename);
            // 如果要在压缩包中创建目录，只需要在条目名中使用 “/” 分隔符即可创建目录
            // ZipEntry zipEntry = new ZipEntry("xxx/" + itemFilename);

            zipOutputStream.putNextEntry(zipEntry);

            // 将数据写入当前条目中
            zipOutputStream.write(itemExcelByte);

            // 关闭当前条目
            zipOutputStream.closeEntry();
        }
        // 完成zip写入，刷新并关闭zip流及其内部的流
        zipOutputStream.close();
    }


    /**
     * 同时导出多个Excel, 然后将他们写入到一个 zip 压缩包中, 返回压缩包的二进制数据
     *
     * @param multiExcelNameList 导出的多个excel的名字 列表
     * @param multiExcelByteList 导出的多个excel的二进制字节 列表
     */
    public static byte[] exportMultiExcelToZipByte(List<String> multiExcelNameList, List<byte[]> multiExcelByteList) throws IOException {

        // zip 输出流, 将zip写到ByteArray输出流中
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        ZipOutputStream zipOutputStream = new ZipOutputStream(outputStream);

        // 循环写入所有的 excel
        for (int i = 0; i < multiExcelNameList.size(); i++) {
            // excel的文件名称
            String itemFilename = multiExcelNameList.get(i);
            // excel的二进制
            byte[] itemExcelByte = multiExcelByteList.get(i);

            // 向 zip 中添加一个条目(文件)
            ZipEntry zipEntry = new ZipEntry(itemFilename);
            // 如果要在压缩包中创建目录，只需要在条目名中使用 “/” 分隔符即可创建目录
            // ZipEntry zipEntry = new ZipEntry("xxx/" + itemFilename);

            zipOutputStream.putNextEntry(zipEntry);

            // 将数据写入当前条目中
            zipOutputStream.write(itemExcelByte);

            // 关闭当前条目
            zipOutputStream.closeEntry();
        }
        // 完成zip写入，刷新并关闭zip流及其内部的流
        zipOutputStream.close();
        // 返回zip文件的二进制
        return outputStream.toByteArray();
    }


    /**
     * 测试
     */
    public static void main(String[] args) throws Exception {

        ArrayList<String> multiExcelNameList = new ArrayList<>();
        ArrayList<byte[]> multiExcelByteList = new ArrayList<>();

        // 创建excel
        for (int i = 0; i < 4; i++) {
            // 如果要在压缩包中创建目录，只需要在文件名中使用 “/” 分隔符即可创建目录
            multiExcelNameList.add(String.format("%d/%s.xlsx", i % 2, "test" + i));
            // multiExcelNameList.add(String.format("%s.xlsx", "test" + i));
            multiExcelByteList.add(getOneTestExcelByteArray());
        }

        // 生成的zip,并返回其二进制数据
        byte[] bytes = exportMultiExcelToZipByte(multiExcelNameList, multiExcelByteList);

        // 写入文件中
        Files.write(Paths.get("D:\\test.zip"), bytes);
    }


    /**
     * 生成一个测试 excel，并返回其二进制数据
     */
    public static byte[] getOneTestExcelByteArray() {
        // 假数据
        ArrayList<Object> data = new ArrayList<>();
        int nextInt = new Random().nextInt(15);
        for (int i = 0; i < 50000; i++) {
            data.add(Arrays.asList("A" + i, nextInt + 1));
            data.add(Arrays.asList("B" + i, nextInt + 2));
            data.add(Arrays.asList("C" + i, nextInt + 3));
        }

        // 定义一个输出流
        ByteArrayOutputStream tempOutput = new ByteArrayOutputStream();

        // 将 excel 写入输出流
        FastExcel.write(tempOutput).sheet(0).doWrite(data);

        // 返回 excel 的二进制
        return tempOutput.toByteArray();
    }


}
